/******************************************************/
/**					fork.c by doom119				  */
/**						May, 2010				      */
/******************************************************/
#include <task.h>
#include <descriptor.h>
#include <mm.h>

long last_pid = 0;

int set_process(int nr, struct task *p)
{
	unsigned long new_code_base, new_data_base;
	unsigned long old_data_base, data_limit;

	data_limit = get_limit(0x17);
	old_data_base = get_base(current->ldt[2]);
	new_code_base = new_data_base = nr * 0x4000000;
	p->start_code = new_code_base;
	set_base(p->ldt[1], new_code_base);
	set_base(p->ldt[2], new_data_base);
	
	if(copy_page_tables(old_data_base, new_data_base, data_limit))
	{
		free_page_tables(new_data_base, data_limit);
		return -1;
	}
	return 0;
}

int copy_process(int nr, long ebp, long edi, long esi, long gs, 
		long ret_addr, long ebx, long ecx, long edx, long fs,
		long es, long ds, long eip, long cs, long eflags, long esp, long ss)
{
	struct task *p;
	unsigned long *addr;

	if(!(addr = (unsigned long *)get_free_page()))
		return 1;

	p = (struct task*)addr;
	task[nr] = p;
	//*p = *current; //wrong in bochs 2.4.2
	p->counter = current->counter;
	p->priority = current->priority;
	p->ldt[1] = current->ldt[1];
	p->ldt[2] = current->ldt[2];

	p->state = TASK_UNINTERRUPTIBLE;
	p->counter = p->priority = 15;
	p->signal = 0;
	p->pid = last_pid;
	p->father = current->pid;
	p->utime = p->stime = 0;

	p->tss.back_link = 0;
	p->tss.esp0 = PAGE_SIZE + (unsigned long)p;
	p->tss.ss0 = 0x10;
	p->tss.eip = eip;
	p->tss.eflags = eflags;
	p->tss.eax = 0;
	p->tss.ecx = ecx;
	p->tss.edx = edx;
	p->tss.ebx = ebx;
	p->tss.esp = esp;
	p->tss.ebp = ebp;
	p->tss.esi = esi;
	p->tss.edi = edi;
	p->tss.es = es & 0xffff;
	p->tss.cs = cs & 0xffff;
	p->tss.ss = ss & 0xffff;
	p->tss.ds = ds & 0xffff;
	p->tss.fs = fs & 0xffff;
	p->tss.gs = gs & 0xffff;
	p->tss.ldt = _LDT(nr); 
	p->tss.trace_bitmap = 0x80000000;

	if(set_process(nr, p))
	{
		free_page((unsigned long)p);
		task[nr] = 0;
		return -1;
	}

	set_tss_desc(_gdt + FIRST_TSS_ENTRY + (nr << 1), &(p->tss));
	set_ldt_desc(_gdt + FIRST_LDT_ENTRY + (nr << 1), &(p->ldt));
	
	p->state = TASK_RUNNING;

	return last_pid;
}

int find_empty_process()
{
	int i, tmp;
	int nr = 0;

	tmp = 0;
	do
	{
		if(++last_pid < 0) last_pid = 1;
		for(i = 0; i < NR_TASKS; ++i)
		{
			if(task[i] && task[i]->pid == last_pid)
				tmp = 1;
			else if(!task[i])
				nr = i;
		}
	} while(tmp);

	//nr can't be 0, because task0 is always running
	if(nr) return nr;

	return -1;

}
